// $codepro.audit.disable logExceptions
/*
 *  View.java 
 *    
 *    Copyright 2012 YAWP Project Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.googlecode.yawp.view;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Calendar;
import java.util.Date;
import java.util.Observable;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.WindowConstants;

import com.googlecode.yawp.model.Model;
import com.googlecode.yawp.model.User;
import com.googlecode.yawp.model.WeightEntry;

/**
 * View handles the view aspects of the MVC design architecture.
 * 
 * @author ccato
 * @author Dan Spuches
 *
 */
public class View extends Observable implements ActionListener {
	
	private Model model;
	// View Windows
	private MainWindow mw;
	private UserLoginWindow ulw = null;
	private ChartWindow cw;
	private WeightEntryWindow wew = null;
	private PreferencesWindow pw = null;
	private JFrame[] windows = { ulw, cw, wew, pw };
	/**
	 * Simple Yes No Answer
	 * @author ccato
	 *
	 */
	public enum Answer { YES, NO }; 
	
	/**
	 * Constructor that requires a model as a parameter.
	 * 
	 * @param model Reference to the model for this application
	 */
	public View(Model model) {
		this.model = model;
	}

	
	/**
	 * Displays and handles events for the - Main Window View
	 */
	public void showMainView() {
		mw = new MainWindow(model);
		mw.setVisible(true);
		mw.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		
		mw.addWindowListener(new WindowListener() {

			@Override
			public void windowActivated(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowClosed(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowClosing(WindowEvent e) {
				if (View.this.ulw != null) { ulw.dispose(); }
				if (View.this.cw != null) { cw.dispose(); }
				if (View.this.wew != null) { wew.dispose(); }
				if (View.this.pw != null) { pw.dispose(); } 
				
				setChanged();
				notifyObservers("SHUTDOWN");
				mw.dispose();
				
			}

			@Override
			public void windowDeactivated(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowDeiconified(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowIconified(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void windowOpened(WindowEvent e) {
				// TODO Auto-generated method stub
				
			}
			
		});
		
		// Register Listener for New User Button to show the New User View 
		mw.setLoginActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				View.this.showUserLoginView();
			}
			
		});
	
		// Register Listener for Chart Button to Show the Chart View
		mw.setChartActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				View.this.showChartView();
			}
			
		});

		// Register Listener for Enter Weight Button to Show the Enter Weight View
		mw.setWeightEntryActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				View.this.showWeightEntryView();
			}
			
		});

		// Register Listener for Enter Preferences Button to Show the Preferences View
		mw.setPreferencesActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				View.this.showPreferencesView();
			}
			
		});

		
		// Register Listener for Close Button to Allow a clean Shutdown
		mw.setCloseActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// close any existing windows
				// TODO maybe should consider some sort of factory pattern?
				if (View.this.ulw != null) { ulw.dispose(); }
				if (View.this.cw != null) { cw.dispose(); }
				if (View.this.wew != null) { wew.dispose(); }
				if (View.this.pw != null) { pw.dispose(); } 
				
				setChanged();
				notifyObservers("SHUTDOWN");
				mw.dispose();
			}
			
		});

		
	}
		
	/**
	 * Displays and handles events for the - User Login View
	 */
	public void showUserLoginView() {
		
		// if a user login window has never been created, create one
		if (ulw == null) {
			ulw = new UserLoginWindow();
		}
		
		// if there is not currently a user login window open...
		if (!ulw.isDisplayable()) {
			ulw.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			ulw.setVisible(true);
			
			ulw.addWindowListener(new WindowListener() {

				@Override
				public void windowActivated(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosed(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosing(WindowEvent e) {
					View.this.ulw.dispose();
					ulw = null;
					
				}

				@Override
				public void windowDeactivated(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowDeiconified(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowIconified(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowOpened(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}
				
			});


			// Register Listener For User Login Button - send user to controller
			ulw.addUserLoginListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					View.this.setChanged();
					User user = new User(View.this.ulw.getUserText(), null);
					View.this.ulw.dispose();
					ulw = null;
					View.this.notifyObservers(user);

				}

			});		

		}
	}
	
	/**
	 * Displays and handles events for the - Chart View
	 */
	public void showChartView() {
		cw = new ChartWindow(model);
		cw.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		cw.setVisible(true);
		
		//chart window has no controller notifications
	}
	
	/**
	 * Displays and handles events for the - Weight Entry View
	 */
	public void showWeightEntryView() {
		// if a weight entry window has never been created, create one
		if (wew == null) {
			wew = new WeightEntryWindow(model);
		}
		
		// if there is not a weight entry window open...
		if(!wew.isDisplayable()) {
			wew.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			wew.setVisible(true);
			
			wew.addWindowListener(new WindowListener() {

				@Override
				public void windowActivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosed(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosing(WindowEvent arg0) {					
					wew.dispose();
					wew = null;
				}

				@Override
				public void windowDeactivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowDeiconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowIconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowOpened(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
			});

			// Register Listener For Add Weight & close Button - send weight to controller
			wew.addNewWeightAndCloseListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					notifyAddWeight();
					View.this.wew.dispose();
					wew = null;
				}

			});

			// Register Listener For Add Weight Button - send weight to controller
			wew.addNewWeightListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					notifyAddWeight();
					Date date = View.this.wew.getDate();
					Calendar cal = Calendar.getInstance();
					cal.setTime(date);
					wew.updateCalendar(cal);
					//View.this.wew.dispose();
				}

			});


			wew.addDeleteWeightListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					WeightEntry weight;

					View.this.setChanged();
					Date date = View.this.wew.getDate();
					Calendar cal = Calendar.getInstance();
					cal.setTime(date);

					weight = new WeightEntry(cal, Double.valueOf(View.this.wew.getWeight()), true);

					View.this.notifyObservers(weight);
					wew.updateCalendar(cal);
					//View.this.wew.dispose();
				}

			});
			
			// Register Listener For Add Weight Button - send weight to controller
			wew.addCloseListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					wew.dispose();
					wew = null;
				}

			});
			
		}
	}
	
	/**
	 * Private helper method that is invoked by the add weight listeners.  This functionality is common to 
	 * both "Add Weight & Close" as well as "Add Weight"
	 */
	private void notifyAddWeight() {
		WeightEntry weight;
		
		setChanged();
		Date date = View.this.wew.getDate();
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		//WeightEntry weight = new WeightEntry(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE), Double.valueOf(View.this.wew.getWeight()));
		if(View.this.wew.getWeight().equalsIgnoreCase("")) {
			// weight string is empty and will cause an issue with Double
			//weight = new WeightEntry(cal, 0, false);
			View.displayErrorMessage("Weight cannot be empty.");
		} else {
			try {
				weight = new WeightEntry(cal, Double.parseDouble(View.this.wew.getWeight()), false);
				notifyObservers(weight);
			} catch (NumberFormatException e) {
				View.displayErrorMessage(wew.getWeight() + " is not a valid weight!");
			}
		}
		
	}
	
	/**
	 * Displays and handles events for the - Preferences Window View
	 */
	public void showPreferencesView() {
		// if a preferences window has never been created
		if (pw == null) {
			pw = new PreferencesWindow(model);
		}
		
		// if there is currently no preferences window open...
		if (!pw.isDisplayable()) {
			pw.setVisible(true);
			pw.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			
			pw.addWindowListener(new WindowListener() {

				@Override
				public void windowActivated(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosed(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosing(WindowEvent e) {
					pw.dispose();
					pw = null;
					
				}

				@Override
				public void windowDeactivated(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowDeiconified(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowIconified(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowOpened(WindowEvent e) {
					// TODO Auto-generated method stub
					
				}
				
			});
			
			// Register Listener for New OK Button to update Preferences if needed
			pw.addPreferencesOKListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {

					//User user = new User(View.this.ulw.getUserText(), null);
					//View.this.notifyObservers(user);
					//View.this.ulw.dispose();							

					if (pw.validatePreferences()) {
						View.this.setChanged();
						View.this.notifyObservers(View.this.pw.getPreferences());
						View.this.pw.dispose();
						pw = null;
					}
				}

			});

			// Register Listener for New Cancel Button to discard preference changes
			pw.addPreferencesCancelListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {

					//User user = new User(View.this.ulw.getUserText(), null);
					//View.this.notifyObservers(user);
					//View.this.ulw.dispose();							


					//View.this.setChanged();
					//View.this.notifyObservers(View.this.pw.getPreferences());
					View.this.pw.dispose();
					pw = null;
				}

			});
		
		}
	}

	/**
	 * This method displays a yes/no dialog to the user which allows the program to branch
	 * based on user input.
	 * 
	 * @param question The question to display to the user
	 * @return Enum<Answer>, either Answer.YES or Answer.NO
	 */
	public Answer displayYesNoMessage(String question) {
		int n = JOptionPane.showConfirmDialog(
				null,
			    question,
			    "Question?",
			    JOptionPane.YES_NO_OPTION);
		if (n == 1) {
			return Answer.NO;
		} else {
			return Answer.YES;
		}
	}
	
	
	/**
	 * Displays a warning message to the user.
	 * 
	 * @param message Message to display to the user.
	 */
	public static void displayWarningMessage(String message) {
		JOptionPane.showMessageDialog(
				null,
			    message,
			    "Warning",
			    JOptionPane.WARNING_MESSAGE);
	}
	
	/**
	 * Displays an error message to the user.
	 * 
	 * @param message Message to display to the user.
	 */
	public static void displayErrorMessage(String message) {
		JOptionPane.showMessageDialog(
				null,
			    message,
			    "Error",
			    JOptionPane.ERROR_MESSAGE);
	}
	
	/**
	 *  Main view event processor for any events not process by the anonymous class listeners
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("VIEW Event Not Handled: " + e.getActionCommand() + ":" + e.paramString());
	}
	
}
